package com.dagene.weixin.util;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import net.sf.json.JSONException;
import net.sf.json.JSONObject;

import org.apache.http.HttpStatus;

import com.dagene.weixin.bean.AccessToken;
import com.dagene.weixin.bean.Menu;

public class HttpUtil {
	// 获取access_token的接口地址（GET） 限200（次/天）
	public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";

	// 菜单创建（POST） 限100（次/天）
	public static String menu_create_url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";

	/**
	 * 创建菜单
	 * 
	 * @param menu
	 *            菜单实例
	 * @param accessToken
	 *            有效的access_token
	 * @return 0表示成功，其他值表示失败
	 */
	public int createMenu(Menu menu, String accessToken) {
		int result = 0;

		// 拼装创建菜单的url
		String url = menu_create_url.replace("ACCESS_TOKEN", accessToken);
		// 将菜单对象转换成json字符串
		String jsonMenu = JSONObject.fromObject(menu).toString();
		// 调用接口创建菜单
		JSONObject jsonObject = httpRequest(url, "POST", jsonMenu);

		if (null != jsonObject) {
			if (0 != jsonObject.getInt("errcode")) {
				result = jsonObject.getInt("errcode");
				// log.error("创建菜单失败 errcode:{} errmsg:{}",
				// jsonObject.getInt("errcode"),
				// jsonObject.getString("errmsg"));
			}
		}

		return result;
	}

	static long lasttimeAccessToken = 0l;
	static AccessToken accessToken = null;

	public static AccessToken getAccessToken() {
		return accessToken;
	}

	public static void setAccessToken(AccessToken accessToken) {
		HttpUtil.accessToken = accessToken;
	}

	/**
	 * 获取access_token
	 * 
	 * @param appid
	 *            凭证
	 * @param appsecret
	 *            密钥
	 * @return
	 */
	public AccessToken getAccessToken(String appid, String appsecret) {
		if ((System.currentTimeMillis() - lasttimeAccessToken) / 1000 >= 5000
				|| accessToken == null) {
			// AccessToken accessToken = null;
			String requestUrl = access_token_url.replace("APPID", appid)
					.replace("APPSECRET", appsecret);
			JSONObject jsonObject = httpGetRequest(requestUrl, "GET");

			// 如果请求成功
			// for (int i = 0;i<2;i++) {
			if (null != jsonObject) {
				try {
					lasttimeAccessToken = System.currentTimeMillis();
					accessToken = new AccessToken();
					accessToken.setToken(jsonObject.getString("access_token"));
					accessToken.setExpiresIn(jsonObject.getInt("expires_in"));
					// break;
				} catch (JSONException e) {
					accessToken = null;
					// jsonObject = httpGetRequest(requestUrl, "GET");
					// 获取token失败
					// log.error("获取token失败 errcode:{} errmsg:{}",
					// jsonObject.getInt("errcode"),
					// jsonObject.getString("errmsg"));
				}
			}
			// }
		}
		return accessToken;
	}

	public JSONObject httpGetRequest(String requestUrl, String requestMethod) {
		return httpRequest(requestUrl, "GET", null);
	}

	public JSONObject httpPostRequest(String requestUrl, String requestMethod,
			String outputStr) {
		return httpRequest(requestUrl, "POST", outputStr);
	}

	/**
	 * 发起https请求并获取结果
	 * 
	 * @param requestUrl
	 *            请求地址
	 * @param requestMethod
	 *            请求方式（GET、POST）
	 * @param outputStr
	 *            提交的数据
	 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
	 */
	public JSONObject httpRequest(String requestUrl, String requestMethod,
			String outputStr) {
		JSONObject jsonObject = null;
		StringBuffer buffer = new StringBuffer();
		try {
			// 创建SSLContext对象，并使用我们指定的信任管理器初始化
			TrustManager[] tm = getTrustingManager();
			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());
			// 从上述SSLContext对象中得到SSLSocketFactory对象
			SSLSocketFactory ssf = sslContext.getSocketFactory();

			URL url = new URL(requestUrl);
			HttpsURLConnection httpUrlConn = (HttpsURLConnection) url
					.openConnection();
			httpUrlConn.setSSLSocketFactory(ssf);

			httpUrlConn.setDoOutput(true);
			httpUrlConn.setDoInput(true);
			httpUrlConn.setUseCaches(false);
			// 设置请求方式（GET/POST）
			httpUrlConn.setRequestMethod(requestMethod);

			if ("GET".equalsIgnoreCase(requestMethod))
				httpUrlConn.connect();

			// 当有数据需要提交时
			if (null != outputStr) {
				OutputStream outputStream = httpUrlConn.getOutputStream();
				// 注意编码格式，防止中文乱码
				outputStream.write(outputStr.getBytes("UTF-8"));
				outputStream.close();
			}

			// 将返回的输入流转换成字符串
			InputStream inputStream = httpUrlConn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(
					inputStream, "utf-8");
			BufferedReader bufferedReader = new BufferedReader(
					inputStreamReader);

			String str = null;
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}
			bufferedReader.close();
			inputStreamReader.close();
			// 释放资源
			inputStream.close();
			inputStream = null;
			httpUrlConn.disconnect();
			jsonObject = JSONObject.fromObject(buffer.toString());
		} catch (ConnectException ce) {
			// log.error("Weixin server connection timed out.");
		} catch (Exception e) {
			e.printStackTrace();
			// log.error("https request error:{}", e);
		}
		return jsonObject;
	}

	public String httpRequestForXML(String requestUrl, String requestMethod,
			String outputStr) {
		StringBuffer buffer = new StringBuffer();
		try {
			// 创建SSLContext对象，并使用我们指定的信任管理器初始化
			TrustManager[] tm = getTrustingManager();
			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());
			// 从上述SSLContext对象中得到SSLSocketFactory对象
			SSLSocketFactory ssf = sslContext.getSocketFactory();

			URL url = new URL(requestUrl);
			HttpsURLConnection httpUrlConn = (HttpsURLConnection) url
					.openConnection();
			httpUrlConn.setSSLSocketFactory(ssf);

			httpUrlConn.setDoOutput(true);
			httpUrlConn.setDoInput(true);
			httpUrlConn.setUseCaches(false);
			// 设置请求方式（GET/POST）
			httpUrlConn.setRequestMethod(requestMethod);

			if ("GET".equalsIgnoreCase(requestMethod))
				httpUrlConn.connect();

			// 当有数据需要提交时
			if (null != outputStr) {
				OutputStream outputStream = httpUrlConn.getOutputStream();
				// 注意编码格式，防止中文乱码
				outputStream.write(outputStr.getBytes("UTF-8"));
				outputStream.close();
			}

			// 将返回的输入流转换成字符串
			InputStream inputStream = httpUrlConn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(
					inputStream, "utf-8");
			BufferedReader bufferedReader = new BufferedReader(
					inputStreamReader);

			String str = null;
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}
			bufferedReader.close();
			inputStreamReader.close();
			// 释放资源
			inputStream.close();
			inputStream = null;
			httpUrlConn.disconnect();
			return buffer.toString();
		} catch (ConnectException ce) {
			// log.error("Weixin server connection timed out.");
		} catch (Exception e) {
			e.printStackTrace();
			// log.error("https request error:{}", e);
		}
		return null;
	}


	/**
	 * 发起https请求上传素材文件
	 * 
	 * @param requestUrl
	 *            请求地址
	 * @param file
	 *            上传的文件
	 * @return json字符串
	 */
	public String httpsRequestForMedia(String requestUrl, File file)
			throws IOException {
		String result = null;
		StringBuffer buffer = new StringBuffer();
		try {
			// 创建SSLContext对象，并使用我们指定的信任管理器初始化
			TrustManager[] tm = getTrustingManager();
			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());
			// 从上述SSLContext对象中得到SSLSocketFactory对象
			SSLSocketFactory ssf = sslContext.getSocketFactory();

			URL url = new URL(requestUrl);
			HttpsURLConnection httpUrlConn = (HttpsURLConnection) url
					.openConnection();
			httpUrlConn.setSSLSocketFactory(ssf);

			// 设置请求方式（POST）
			httpUrlConn.setRequestMethod("POST");
			httpUrlConn.setDoOutput(true);
			httpUrlConn.setDoInput(true);
			httpUrlConn.setUseCaches(false);
			// 设置请求头信息
			httpUrlConn.setRequestProperty("Connection", "Keep-Alive");
			httpUrlConn.setRequestProperty("Charset", "UTF-8");

			// 设置边界
			String BOUNDARY = "----------" + System.currentTimeMillis();
			httpUrlConn.setRequestProperty("Content-Type",
					"multipart/form-data; boundary=" + BOUNDARY);

			// 请求正文信息
			// 第一部分：
			StringBuilder sb = new StringBuilder();
			sb.append("--"); // 必须多两道线
			sb.append(BOUNDARY);
			sb.append("\r\n");
			sb.append("Content-Disposition: form-data;name=\"file\";filename=\""
					+ file.getName() + "\"\r\n");
			sb.append("Content-Type:application/octet-stream\r\n\r\n");

			byte[] head = sb.toString().getBytes("utf-8");

			// 获得输出流
			OutputStream out = new DataOutputStream(
					httpUrlConn.getOutputStream());
			// 输出表头
			out.write(head);

			// 文件正文部分
			// 把文件以流的方式 推入到url中
			DataInputStream in = new DataInputStream(new FileInputStream(file));
			int bytes = 0;
			byte[] bufferOut = new byte[1024];
			while ((bytes = in.read(bufferOut)) != -1) {
				out.write(bufferOut, 0, bytes);
			}
			in.close();
			// 结尾部分
			byte[] foot = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("utf-8");// 定义最后数据分隔线
			out.write(foot);
			out.flush();
			out.close();

			BufferedReader reader = null;
			try {
				// 定义BufferedReader输入流来读取URL的响应
				reader = new BufferedReader(new InputStreamReader(
						httpUrlConn.getInputStream()));
				String line = null;

				while ((line = reader.readLine()) != null) {
					// System.out.println(line);
					buffer.append(line);
				}
				// if (result == null) {
				result = buffer.toString();
				// }
			} catch (IOException e) {

				System.out.println("发送POST请求出现异常！" + e);
				e.printStackTrace();
				throw new IOException("数据读取异常");
			} finally {
				if (reader != null) {

					reader.close();
				}
			}

			httpUrlConn.disconnect();
		} catch (ConnectException ce) {
			ce.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	// 下载多媒体文件
	public String downloadPDFFile(String requestUrl, String savePath) {

		try {
			URL url = new URL(requestUrl);

			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setDoInput(true);
			conn.setRequestMethod("GET");

			if (!savePath.endsWith("/")) {
				savePath += "/";
			}

			// 返回状态码
			int statusCode = conn.getResponseCode();
			// 正确情况下
			if (statusCode == HttpStatus.SC_OK) {
				String filename = UUID.randomUUID().toString();
				String filePath = savePath + filename + ".pdf";
				// 打开连接
				URLConnection con = url.openConnection();
				// 输入流
				InputStream is = con.getInputStream();
				// 1K的数据缓冲
				byte[] bs = new byte[1024];
				// 读取到的数据长度
				int len;
				// 输出的文件流
				OutputStream os = new FileOutputStream(filePath);
				// 开始读取
				while ((len = is.read(bs)) != -1) {
					os.write(bs, 0, len);
				}
				// 完毕，关闭所有链接
				os.close();
				is.close();
				return Constants.NowweixinUrl + "/tmp/" + filename + ".pdf";

			} else {

				throw new Exception("http request state error! statusCode="
						+ statusCode);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;

	}

	// 下载多媒体文件
	public String downloadMediaFile(String requestUrl, String savePath) {

		try {
			URL url = new URL(requestUrl);

			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setDoInput(true);
			conn.setRequestMethod("GET");

			if (!savePath.endsWith("/")) {
				savePath += "/";
			}

			// 返回状态码
			int statusCode = conn.getResponseCode();
			// 正确情况下
			if (statusCode == HttpStatus.SC_OK) {

				// 根据内容类型获取扩展名
				// String fileExt =
				// FileUtil.getFileEndWitsh(conn.getHeaderField("Content-Type"));
				// 获取文件名
				String disposition = conn.getHeaderField("Content-disposition");
				String[] ss = disposition.split("filename=");
				String filename = ss[1].substring(1, ss[1].length() - 1);

				String filePath = savePath + filename;

				InputStream in = conn.getInputStream();
				FileOutputStream out = new FileOutputStream(new File(filePath));
				byte[] buffer = new byte[1024];
				int byteRead;

				while ((byteRead = in.read(buffer)) != -1) {

					out.write(buffer, 0, byteRead);
				}

				if (out != null) {
					out.close();
				}
				if (in != null) {
					in.close();
					in = null;
				}
				conn.disconnect();
				System.out.println(filePath);
				return filename;

			} else {

				throw new Exception("http request state error! statusCode="
						+ statusCode);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;

	}

	// 下载多媒体文件
	public String downloadVoiceFile(String requestUrl, String savePath) {

		try {
			URL url = new URL(requestUrl);

			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setDoInput(true);
			conn.setRequestMethod("GET");

			if (!savePath.endsWith("/")) {
				savePath += "/";
			}

			// 返回状态码
			int statusCode = conn.getResponseCode();
			// 正确情况下
			if (statusCode == HttpStatus.SC_OK) {

				// 根据内容类型获取扩展名
				// String fileExt =
				// FileUtil.getFileEndWitsh(conn.getHeaderField("Content-Type"));
				// 获取文件名
				String disposition = conn.getHeaderField("Content-disposition");
				String[] ss = disposition.split("filename=");
				String filename = ss[1].substring(1, ss[1].length() - 1);

				String filePath = savePath + filename;
				InputStream in = conn.getInputStream();
				FileOutputStream out = new FileOutputStream(new File(filePath));
				byte[] buffer = new byte[1024];
				int byteRead;

				while ((byteRead = in.read(buffer)) != -1) {

					out.write(buffer, 0, byteRead);
				}

				if (out != null) {
					out.close();
				}
				if (in != null) {
					in.close();
					in = null;
				}
				conn.disconnect();
				String[] filenames = filename.split("amr");
				String targetPath = savePath + filenames[0] + "mp3";
				MediaUtil.changeToMp3(filePath, targetPath);
				System.out.println(filePath);
				return filenames[0] + "mp3";

			} else {

				throw new Exception("http request state error! statusCode="
						+ statusCode);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;

	}

	public void sendPostXML(String urlStr, String xml) {
		try {
			URL url = new URL(urlStr);
			URLConnection con = url.openConnection();
			con.setDoOutput(true);
			con.setRequestProperty("Pragma:", "no-cache");
			con.setRequestProperty("Cache-Control", "no-cache");
			con.setRequestProperty("Content-Type", "text/xml");

			OutputStreamWriter out = new OutputStreamWriter(
					con.getOutputStream());
			out.write(new String(xml.getBytes("ISO-8859-1")));
			out.flush();
			out.close();
			BufferedReader br = new BufferedReader(new InputStreamReader(
					con.getInputStream(), "UTF-8"));
			String line = "";
			for (line = br.readLine(); line != null; line = br.readLine()) {
				System.out.println(line);
			}
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public List<String> httpRequestNoSsl(String requestUrl,
			String requestMethod, String outputStr) {
		List<String> buffer = new ArrayList<String>();
		try {
			URL url = new URL(requestUrl);
			HttpURLConnection httpUrlConn = (HttpURLConnection) url
					.openConnection();
			httpUrlConn.setDoOutput(true);
			httpUrlConn.setDoInput(true);
			httpUrlConn.setUseCaches(false);
			// 设置请求方式（GET/POST）
			httpUrlConn.setRequestMethod(requestMethod);
			if ("GET".equalsIgnoreCase(requestMethod))
				httpUrlConn.connect();

			// 当有数据需要提交时
			if (null != outputStr) {
				OutputStream outputStream = httpUrlConn.getOutputStream();
				// 注意编码格式，防止中文乱码
				outputStream.write(outputStr.getBytes("UTF-8"));
				outputStream.close();
			}

			// 将返回的输入流转换成字符串
			InputStream inputStream = httpUrlConn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(
					inputStream, "utf-8");
			BufferedReader bufferedReader = new BufferedReader(
					inputStreamReader);

			String str = null;
			int count = 0;
			while ((str = bufferedReader.readLine()) != null) {
				if (str.indexOf("<string>") >= 0) {
					if (count == 1 || count == 5 || count == 6 || count == 7
							|| count == 8 || count == 10) {
						str = str.replaceAll("<string>", "").replaceAll(
								"</string>", "");
						buffer.add(str);
						// if (count==6 || count==8)
					}
					count++;
				}
			}
			bufferedReader.close();
			inputStreamReader.close();
			// 释放资源
			inputStream.close();
			inputStream = null;
			httpUrlConn.disconnect();
		} catch (ConnectException ce) {
			// log.error("Weixin server connection timed out.");
		} catch (Exception e) {
			e.printStackTrace();
			// log.error("https request error:{}", e);
		}
		return buffer;
	}

	/**
	 * 获取access_token是通过GET方式访问如下链接： [java] view plaincopy
	 * https://api.weixin.qq.
	 * com/cgi-bin/token?grant_type=client_credential&appid
	 * =APPID&secret=APPSECRET 链接中有三个参数，分别是grant_type、appid和secret。根据图中的参数说明，
	 * grant_type传固定值client_credential， 而appid和secret就是申请完自定义菜单后微信分配给我们的。
	 * 请求发送成功后，微信服务器会返回一个json串，包含access_token和expires_in两个元素。
	 * 其中，access_token就是我们最终需要的凭证，而expires_in是凭证的有效期，单位是秒，7200秒也就是2个小时。
	 * 这就意味着，不是每次访问特殊接口，都需要重新获取一次access_token，只要access_token还在有效期内，就一直可以使用。
	 */
	/*
	 * public String getAccessToken(String appId, String secret) { HttpClient
	 * httpclient = new DefaultHttpClient(); try { StringBuffer sb = new
	 * StringBuffer(
	 * "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
	 * ); sb.append(appId).append("&secret=").append(secret);
	 * 
	 * 
	 * SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null,
	 * getTrustingManager(), new java.security.SecureRandom());
	 * 
	 * SSLSocketFactory socketFactory = new SSLSocketFactory(sc); Scheme sch =
	 * new Scheme("https", 443, socketFactory);
	 * httpclient.getConnectionManager().getSchemeRegistry().register(sch);
	 * HttpGet httpget = new HttpGet(sb.toString()); HttpResponse response =
	 * httpclient.execute(httpget); HttpEntity entity = response.getEntity(); if
	 * (entity != null) { JsonParser jsonParser = new
	 * JsonFactory().createJsonParser(EntityUtils.toString(entity));
	 * while(jsonParser.nextToken() != JsonToken.END_OBJECT){ String name =
	 * jsonParser.getCurrentName(); if("access_token".equals(name)){
	 * jsonParser.nextToken(); return jsonParser.getText(); } else
	 * if("errmsg".equals(name)){ jsonParser.nextToken();
	 * System.out.println(jsonParser.getText()); } } //JSONObject a = new
	 * JSONObject(); } } catch(Exception ex) { } finally {
	 * httpclient.getConnectionManager().shutdown(); } return ""; }
	 */

	private TrustManager[] getTrustingManager() {
		TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
			@Override
			public java.security.cert.X509Certificate[] getAcceptedIssuers() {
				return null;
			}

			@Override
			public void checkClientTrusted(
					java.security.cert.X509Certificate[] arg0, String arg1)
					throws CertificateException {
				// TODO Auto-generated method stub

			}

			@Override
			public void checkServerTrusted(
					java.security.cert.X509Certificate[] arg0, String arg1)
					throws CertificateException {
				// TODO Auto-generated method stub

			}

		} };
		return trustAllCerts;
	}

	public static void main(String[] args) {
		StringBuffer sb = new StringBuffer(
				"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=");
		sb.append("appid").append("&secret=").append("sectet");
		new HttpUtil().httpRequest(sb.toString(), "GET", null);
		// new HttpUtil().getAccessToken("abc", "efg");
	}

}
